Skip to content

fix(osv): resolve latest version before querying to eliminate false positives#20

Merged
ProduktEntdecker merged 2 commits intomainfrom
fix/19-resolve-latest-version
Apr 19, 2026
Merged

fix(osv): resolve latest version before querying to eliminate false positives#20
ProduktEntdecker merged 2 commits intomainfrom
fix/19-resolve-latest-version

Conversation

@ProduktEntdecker
Copy link
Copy Markdown
Owner

@ProduktEntdecker ProduktEntdecker commented Apr 18, 2026

Summary

  • Resolves latest from the npm/PyPI registry before querying OSV when no version is specified
  • Falls back to the previous unversioned query on registry failure (preserves fail-closed behavior)
  • Surfaces the resolved version in the hook output (vite@8.0.8 instead of misleading vite@latest)

Why

OSV returns vulnerabilities across all versions ever published when no version is given. PatchPilot was treating that result as if it applied to the version about to be installed, surfacing patched CVEs as active threats.

Package Before this PR After this PR (current latest)
vite 5 HIGH (across all versions) 0 — vite@8.0.8 is patched
playwright 1 HIGH (across all versions) 0 — playwright@1.59.1 is patched

Common trigger: npx <tool> for already-installed local binaries (vite, vitest, playwright, …). Hit by Vite itself, Playwright, and — meta-painfully — vitest while testing this very fix.

Test plan

  • 7 new unit tests in src/osv.test.ts covering: npm resolve, scoped npm encoding, PyPI resolve, skip when version provided, 404 fallback, network-error fallback, end-to-end vite reproduction
  • All 110 tests pass (was 103 before)
  • tsc --noEmit clean
  • Manual: confirm npx vite no longer fires the false positive after merge + npm publish
  • Manual: confirm a known-vulnerable version (e.g. npm install lodash@4.17.20) still trips the hook

Closes #19

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Genauere Schwachstellenmeldungen: Bei fehlender Version wird nun versucht, die aktuellste Paketversion aus npm/PyPI zu ermitteln und im Bericht anzuzeigen, statt immer "latest" auszugeben.
  • New Features
    • Ergebnis enthält, wenn verfügbar, die aufgelöste Version zur besseren Nachvollziehbarkeit.
  • Tests
    • Neue Tests zur Validierung von Versionsermittlung und Abfrageverhalten über verschiedene Registry-Szenarien.

…ositives

When a package is referenced without a version (e.g. `npx vite`,
`npx playwright install chromium`), the OSV query was sent without a
version field. OSV then returns vulnerabilities across ALL versions ever
published, surfacing CVEs that have long since been patched.

Real-world impact: `vite@latest` was reported with 5 HIGH vulnerabilities
when the actual current release (`vite@8.0.8`) has zero. Same for
`playwright@latest` (1 HIGH reported, 0 in current `1.59.1`).

Fix: when no version is specified, resolve `latest` from the npm or PyPI
registry first, then query OSV with that concrete version. On registry
failure (404, timeout, network error), fall back to the previous
unversioned query — keeps prior fail-closed behavior intact.

The resolved version is also surfaced through `CheckResult.resolvedVersion`
so the hook output shows `vite@8.0.8` instead of the misleading
`vite@latest`.

Closes #19

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

Walkthrough

Fehlt bei einer Paket-Anfrage die Version, wird jetzt zunächst die aktuelle Version aus npm- oder PyPI-Registry abgefragt und als version an die OSV-Query übergeben. Bei Fehlschlag bleibt das Verhalten erhalten (unversionierte OSV-Query). checkPackageVulnerabilities liefert optional resolvedVersion zurück.

Changes

Cohort / File(s) Summary
Version-Auflösungslogik
src/osv.ts
Neue Funktion resolveLatestVersion(name, ecosystem) mit 2s Abort-Timeout; checkPackageVulnerabilities verwendet bei fehlender version die aufgelöste effectiveVersion und setzt optionales resolvedVersion im Erfolgsergebnis.
Versionsdisplay in Meldungen
src/index.ts
Meldungskonstruktion nutzt nun displayVersion = `pkg.version
Testsuite
src/osv.test.ts
Neue Vitest-Tests: Registry-Resolution für npm/PyPI, scoped-package-Encoding, Vermeidung von Registry-Aufruf bei expliziter Version, Verhalten bei 404/Timeout, und Regressionstest für versionierte vs. unversionierte OSV-Queries.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Client as "index.ts\n(Patcher)"
    participant OSVModule as "osv.ts\n(checkPackageVulnerabilities)"
    participant Registry as "npm/PyPI\nRegistry"
    participant OSV as "OSV API"

    User->>Client: Anfrage (Paket, evtl. ohne Version)
    Client->>OSVModule: checkPackageVulnerabilities(pkg)
    OSVModule->>OSVModule: prüfe: version vorhanden?
    alt version fehlt
        OSVModule->>Registry: GET latest (registry.npmjs.org/... oder pypi.org/...)
        Registry-->>OSVModule: resolvedVersion oder Fehler/404
    end
    alt resolvedVersion vorhanden
        OSVModule->>OSV: POST /v1/query mit package + version = resolvedVersion
    else keine resolvedVersion
        OSVModule->>OSV: POST /v1/query mit package (kein version-Feld)
    end
    OSV-->>OSVModule: vulnerabilities[]
    OSVModule-->>Client: { status: 'success', vulnerabilities, resolvedVersion? }
    Client->>Client: displayVersion = pkg.version || resolvedVersion || "latest"
    Client-->>User: Ergebnismeldung mit displayVersion und Vulnerabilities
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Der Titel beschreibt korrekt die Hauptänderung: Auflösung der aktuellen Version vor der OSV-Abfrage zur Elimination von falschen Positiven.
Linked Issues check ✅ Passed Der PR implementiert alle Anforderungen aus Issue #19: Auflösung von 'latest' aus Registry (npm/PyPI), OSV-Abfrage mit konkreter Version, Fallback bei Registry-Fehler (404, Timeout), und Anzeige der aufgelösten Version im Output.
Out of Scope Changes check ✅ Passed Alle Änderungen sind direkt auf die Anforderung aus #19 ausgerichtet: Registry-Auflösung, OSV-Versioning, Test-Coverage. Keine Out-of-Scope-Änderungen erkannt.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/19-resolve-latest-version

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/osv.test.ts`:
- Around line 1-9: The global fetch stub created with vi.stubGlobal('fetch')
(and the mockFetch fn) must be torn down to avoid cross-test interference: add
an afterEach() that calls vi.unstubGlobal('fetch') (or vi.unstubAllGlobals())
and ensure mockFetch.mockReset() is still called in beforeEach/afterEach as
appropriate; update src/osv.test.ts to add afterEach(() => {
vi.unstubGlobal('fetch'); }) (reference vi.stubGlobal('fetch') and mockFetch) so
the global stub is removed between tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 34d6dc48-c72f-4b6a-aa45-814a67e95374

📥 Commits

Reviewing files that changed from the base of the PR and between fb961f0 and c6ac161.

📒 Files selected for processing (3)
  • src/index.ts
  • src/osv.test.ts
  • src/osv.ts

Comment thread src/osv.test.ts Outdated
@ProduktEntdecker
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

CodeRabbit review on PR #20: the module-scoped vi.stubGlobal('fetch')
persisted across the test file. Move stubbing into beforeEach and add
afterEach with vi.unstubAllGlobals() so each test gets a fresh stub
and the global is restored cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ProduktEntdecker
Copy link
Copy Markdown
Owner Author

@coderabbitai resolve

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/osv.test.ts`:
- Around line 136-150: The test for network-error fallback needs an explicit
assertion that, despite the registry rejection, an unversioned OSV query was
still performed: after calling checkPackageVulnerabilities('vite', undefined,
'npm') assert that mockFetch was invoked for the OSV endpoint (e.g.,
'https://api.osv.dev/v1/query') and that the request payload corresponds to an
unversioned query (no resolved version sent), using the existing mockFetch and
osvResponse helpers to locate and inspect the call.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 90f9fe8d-744c-4ea4-b139-1326b345c189

📥 Commits

Reviewing files that changed from the base of the PR and between c6ac161 and 62e7b6e.

📒 Files selected for processing (1)
  • src/osv.test.ts

Comment thread src/osv.test.ts
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

✅ Actions performed

Comments resolved and changes approved.

@ProduktEntdecker
Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ProduktEntdecker ProduktEntdecker merged commit 126b08c into main Apr 19, 2026
4 checks passed
@ProduktEntdecker ProduktEntdecker deleted the fix/19-resolve-latest-version branch April 19, 2026 14:42
ProduktEntdecker added a commit that referenced this pull request Apr 19, 2026
- Bump version to 0.3.1
- Add CHANGELOG entry for false-positive fix (#19/#20)
- Add README sections: Accuracy (version resolution) and Local `npx <tool>` limitation

Closes #21

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ProduktEntdecker ProduktEntdecker mentioned this pull request Apr 19, 2026
3 tasks
ProduktEntdecker added a commit that referenced this pull request Apr 19, 2026
- Bump version to 0.3.1
- Add CHANGELOG entry for false-positive fix (#19/#20)
- Add README sections: Accuracy (version resolution) and Local `npx <tool>` limitation

Closes #21

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: False positive when no version specified (npx <tool>)

1 participant